package com.youpai.platform.vulture.common.handler;

import com.alibaba.fastjson.JSONObject;
import com.youpai.platform.vulture.common.constant.CustomConstants;
import com.youpai.platform.vulture.common.constant.NumberConstants;
import com.youpai.platform.vulture.common.entity.AccessMessage;
import com.youpai.platform.vulture.common.entity.Header;
import com.youpai.platform.vulture.common.entity.HeaderBytes;
import com.youpai.platform.vulture.common.entity.TowReference;
import com.youpai.platform.vulture.common.enums.MsgFlagEnum;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import io.netty.util.CharsetUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.RandomUtils;

import java.util.ArrayList;
import java.util.List;

import static com.youpai.platform.vulture.common.util.PrimaryKeyUtil.*;


/**
 * @date: 2022/6/22 23:31
 * @author: Anna
 * @version: 1.0
 * @description: 自定义协议编码器
 */
public class CustomMessageEncoder extends MessageToByteEncoder<AccessMessage> {

    @Override
    protected void encode(ChannelHandlerContext ctx, AccessMessage msg, ByteBuf out) {
        try {
            encode(msg, out);
        } finally {
            cleanAllTL();
        }
    }

    private void encode(AccessMessage msg, ByteBuf out) {
        if (!MsgFlagEnum.isSupportFlag(msg.getFlag())) {
            throw new RuntimeException("不支持该类型flag：" + msg.getFlag());
        }
        int total;
        int headerLength = 0;
        int bodyLength = 0;
        byte key = RandomUtils.nextBytes(1)[0];
        // key
        out.writeByte(key);
        keyTL.set(key);
        bufTL.set(out);
        // flag
        writeByte(msg.getFlag());
        // 是心跳包
        if (MsgFlagEnum.HEART_BEAT_PING.flag == msg.getFlag() || MsgFlagEnum.HEART_BEAT_PONG.flag == msg.getFlag()) {
            return;
        }
        List<? extends Header> headerList = msg.getHeaderList();
        if (CollectionUtils.isNotEmpty(headerList)) {
            TowReference<Integer, List<HeaderBytes>> result = calculateHeaderLengthAndEncodeToBytes(headerList);
            headerLength = result.getTheFirst();
            headerList = result.getTheSecond();
        }
        byte[] bodyBytes = null;
        if (null != msg.getBody()) {
            bodyBytes = msg.getBody().getBytes(CharsetUtil.UTF_8);
            bodyLength = bodyBytes.length;
        }
        total = CustomConstants.HEADER_COUNT_LENGTH + headerLength + bodyLength;
        // 长度域
        out.writeShort(total);
        // 头数量
        if (0 != headerLength) {
            writeByte(headerList.size());
            writeHeaderList(headerList);
        } else {
            writeByte(NumberConstants.INT_ZERO);
        }
        if (0 != bodyLength) {
            writeBody(bodyBytes);
        }
    }

    private TowReference<Integer, List<HeaderBytes>> calculateHeaderLengthAndEncodeToBytes(
            List<? extends Header> headerList) {
        int sum = 0;
        List<HeaderBytes> headerBytesList = new ArrayList<>(headerList.size());
        for (Header header : headerList) {
            HeaderBytes headerBytes = new HeaderBytes();
            headerBytes.setHeaderType(header.getHeaderType());
            headerBytes.setHeaderBytes(JSONObject.toJSONString(header.getHeaderValue()).getBytes(CharsetUtil.UTF_8));
            headerBytesList.add(headerBytes);
            sum = sum + 2 + headerBytes.getHeaderBytes().length;
            header = null;
        }
        return new TowReference<>(sum, headerBytesList);
    }

    private void writeBody(byte[] bodyBytes) {
        writeBytes(bodyBytes);
    }

    private void writeHeaderList(List<? extends Header> headerList) {
        for (Header header : headerList) {
            HeaderBytes headerBytes = (HeaderBytes) header;
            writeByte(headerBytes.getHeaderType());
            writeByte(headerBytes.getHeaderBytes().length);
            writeBytes(headerBytes.getHeaderBytes());
        }
    }
}
